/////////////////////////////////////////////////////////
// Frm_3DMath.h
// Declares vector, matrix, rectangles and defines special
// array elements
/////////////////////////////////////////////////////////

#ifndef FRM_MATH_H
#define FRM_MATH_H

#include <memory.h>

namespace Frm {

float ACos(float pValue);

//Vector description/////////////////////////
template <typename T>
class vector {
	public:
		T x, y, z;
      vector(void): x(0), y(0), z(0) {};
      vector(T px, T py, T pz): x(px), y(py), z(pz) {};
     	vector(const vector<T> &pv):x (pv.x), y(pv.y), z(pv.z){};
      vector(const T* &pv){x = pv[0]; y = pv[1]; z = pv[2];};

      vector<T>& operator=(const vector<T> &pv)
      	{x = pv.x; y = pv.y; z = pv.z; return *this;};
      inline void Set(T px, T py, T pz){x=px; y=py; z=pz;};
      bool operator>(const vector<T> &pv){return (x+y)>(pv.x+pv.y);};

      T operator[](int index)const{if (index== 1) return y; if (index== 2)return z; return x;};
      T& operator[](int index){if (index== 1) return y; if (index== 2)return z; return x;};

      vector<T> operator+(vector<T> pv)const {return vector<T>((T)(x + pv.x), (T)(y + pv.y), (T)(z + pv.z));};
      vector<T> operator-(vector<T> pv)const{return vector<T>((T)(x - pv.x), (T)(y - pv.y),(T)(z - pv.z));};
      vector<T> operator*(T pT)const{return vector<T>((T)(pT * x), (T)(pT * y), (T)(pT * z));};

      void Normalize(void){vector<T> pv((T)(x*x),(T)(y*y), (T)(z*z));T fLength = (T)(1.0f/(float)(pv.x + pv.y + pv.z)); if (fLength < 1e-08) return; x = (T)(pv.x * fLength); y = (T)(pv.y * fLength); z = (T)(pv.z * fLength);};
      T Dot(vector<T> pV)const{return (T)(x*pV.x + y*pV.y + z*pV.z);};
      vector<T> Cross(vector<T> pV)const{return vector<T>((T)(y * pV.z - z * pV.y), (T)(z * pV.x - x * pV.z), (T)(x * pV.y - y * pV.x));};
      vector<T> UnitCross(vector<T> pV)const{vector<T> pR((T)(y * pV.z - z * pV.y), (T)(z * pV.x - x * pV.z), (T)(x * pV.y - y * pV.x)); pR.Normalize(); return pR;};
};

template<typename T, int size>
struct Array {
	T data[size];
   Array(void){memset(data, 0, size * sizeof(T));};
   Array(const Array<T, size> &pA){memcpy(data, pA.data, size * sizeof(T));};
   Array(const T* pT){memcpy(data, pT, size * sizeof(T));};

	Array<T, size>& operator=(Array<T, size> pA)
      	{memcpy(data, pA.data, size * sizeof(T)); return *this;};
   Array<T, size>& operator+=(Array<T, size> &pA);
   Array<T, size> operator*(const T pScalar);
   Array<T, size> operator+(Array<T, size> &pA);
   T operator[](int index)const{return data[index];};
   T& operator[](int index){return data[index];};
};

typedef Array<unsigned short, 3> Face;
typedef Array<float, 3> Vertex;
typedef Array<float, 2> TCoord;
typedef Array<float, 4> Color4;
typedef Array<float, 3> Color3;

template<typename T>
Array<T, 4> Set4(T px, T py, T pz, T pw){T data[4]; data[0]=px; data[1]=py; data[2]=pz; data[3]=pw;return Array<T, 4>(data);};

template<typename T>
Array<T, 3> Set3(T px, T py, T pz){T data[3]; data[0]=px; data[1]=py; data[2]=pz;return Array<T, 3>(data);};

template<typename T>
Array<T, 2> Set2(T pu, T pv){T data[2]; data[0]=pu; data[1]=pv;return Array<T, 2>(data);};

/***********************************************
NEW-NEW- NEW- NEW- NEW- NEW- NEW- NEW- NEW- NEW*/

template <typename T>
class matrix {
	public:
   	T data[16];
   	matrix(void){memset(data, 0, 16 * sizeof(T));};
      matrix(const matrix<T> &pm){memcpy(data, pm.data, 16 * sizeof(T));};
      matrix(const T* pT){memcpy(data, pT, 16 * sizeof(T));};
		void Zero(void){memset(data, 0, 16 * sizeof(T));};
      void Identity(void){memset(data, 0, 16 * sizeof(T)); data[0]=(T)1; data[5]=(T)1; data[10]=(T)1; data[15]=(T)1;};
      matrix<T>& operator=(const matrix<T> &pm){memcpy(data, pm.data, 16 * sizeof(T));return *this;};
      matrix<T> operator+(matrix<T> &pm);
      matrix<T> operator-(matrix<T> &pm);
		matrix<T> operator*(matrix<T> &pm);
      matrix<T>& operator*=(matrix<T> &pm);
      matrix<T> operator*(T pT);
      vector<T> operator*(const vector<T> &pV);
      Array<T, 3> operator*(const Array<T, 3> &pV);

      T operator[](int index)const{return data[index];};
      T& operator[](int index){return data[index];};

      T __stdcall Index(int pRow, int pCol)const{return data[(pRow<<2) + pCol];};
      T operator()(int pRow, int pCol)const{return data[(pRow<<2) + pCol];};
      T& operator()(int pRow, int pCol){return data[(pRow<<2) + pCol];};
      vector<T> GetRow(int index){index <<= 2;return vector<T>(data[index++], data[index++], data[index]);};
      vector<T> GetColumn(int index){return vector<T>(data[index], data[index+4], data[index+8]);};

   private:
   	int k, l, row, col;
};

typedef matrix<float> glMatrix;
/***END*****************************************/

//Rectangle description///////////////////////
template <typename T>
class trect {
	public:
   	T xs, ys, xe, ye;
      trect(void): xs(0), ys(0), xe(0), ye(0){};
   	trect(const trect<T> &prect)
   		{xs = prect.xs; ys = prect.ys; xe = prect.xe; ye = prect.ye;};
      trect<T>& operator=(const trect<T> &prect)
      	{xs = prect.xs; ys = prect.ys; xe = prect.xe; ye = prect.ye; return *this;};
      trect<T>& operator+=(const vector<T> &pV)
			{xs += pV.x; ys += pV.y; xe += pV.x; ye += pV.y; return *this;};/**/
/*      trect<T>& operator+=(vector<T> &pV)
			{xs += pV[VX]; ys += pV[VY]; xe += pV[VX]; ye += pV[VY]; return *this;};/**/
      trect(const T pxs, const T pys, const T pxe, const T pye)
      	{xs = pxs; ys = pys; xe = pxe; ye = pye;};
      inline void Set(const T pxs, const T pys, const T pxe, const T pye)
      	{xs = pxs; ys = pys; xe = pxe; ye = pye;};
      bool __stdcall InRect(const T &px, const T &py)
   		{ if (px < xs) return false;
        	  if (px > xe) return false;
        	  if (py < ys) return false;
        	  if (py > ye) return false;
        	  return true;};
   	bool __stdcall InRect(const vector<T> &pv)
   		{ if (pv.x < xs) return false;
        	  if (pv.x > xe) return false;
        	  if (pv.y < ys) return false;
        	  if (pv.y > ye) return false;
        	  return true;};/**/
/*   	bool __stdcall InRect( vector<T> &pv)
   		{ if (pv[VX] < xs) return false;
        	  if (pv[VX] > xe) return false;
        	  if (pv[VY] < ys) return false;
        	  if (pv[VY] > ye) return false;
        	  return true;};/**/
      bool __stdcall InRect(const trect<T> &prect)
      	{ if ((prect.xe <= xe) &&
               (prect.ye <= ye) &&
               (prect.xs >= xs) &&
               (prect.ys >= ys))
               return true;
           return false; }
   	bool __stdcall Intersect(const trect<T> &prect)
   		{ if (prect.xe < xs) return false;
           if (prect.xs > xe) return false;
        	  if (prect.ye < ys) return false;
        	  if (prect.ys > ye) return false;
        	  return true;};
   	bool __stdcall Union(const trect<T> &prect)
   		{ if (!Intersect(prect)) return false;
       	  if (xs < prect.xs) xs = prect.xs;
        	  if (ys < prect.ys) ys = prect.ys;
        	  if (xe > prect.xe) xe = prect.xe;
           if (ye > prect.ye) ye = prect.ye;
		  	  return true;};
};

////// Implementation details ///////////////

template<typename T, int size>
Array<T, size>& Array<T, size>::operator+=(Array<T, size> &pA)
{
   for (int i= 0; i< size; i++)
   	data[i] += pA.data[i];
   return *this;
}

template<typename T, int size>
Array<T, size> Array<T, size>::operator*(const T pScalar)
{
	T rdata[size];
   memcpy(rdata, data, size * sizeof(T));
   for (int i= 0; i< size; i++)
   	rdata[i]*=pScalar;
   return Array<T,size>(rdata);
}

template<typename T, int size>
Array<T, size> Array<T, size>::operator+(Array<T, size> &pA)
{
	T rdata[size];
   for (int i= 0; i< size; i++)
   	rdata[i]=data[i] + pA.data[i];
   return Array<T,size>(rdata);
};

template<typename T>
matrix<T> matrix<T>::operator+(matrix<T> &pm)
{
	T Rdata[16];
   for(k = 0; k < 16; k++)
   	Rdata[k] = data[k] + pm.data[k];
   return matrix<T>(Rdata);
}

template<typename T>
matrix<T> matrix<T>::operator-(matrix<T> &pm)
{
	T Rdata[16];
	for(k = 0; k < 16; k++)
   	Rdata[k] = data[k] - pm.data[k];
   return matrix<T>(Rdata);
}

template<typename T>
matrix<T> matrix<T>::operator*(matrix<T> &pm)
{
	T Rdata[16];
   for (row = 0; row < 16; row +=4)
		for (col = 0; col < 4; col ++)
   		{
         l = 0;
         Rdata[row + col] = 0;
         for (k = 0; k < 4; k++, l += 4)
		   	Rdata[row + col] += data[row + k] * pm.data[l + col];
	      }
   return matrix<T>(Rdata);
}

template<typename T>
matrix<T>& matrix<T>::operator*=(matrix<T> &pm)
{
	T Rdata[16];
   for (row = 0; row < 16; row +=4)
		for (col = 0; col < 4; col ++)
   		{
         l = 0;
         Rdata[row + col] = 0;
         for (k = 0; k < 4; k++, l += 4)
		   	Rdata[row + col] += data[row + k] * pm.data[l + col];
	      }
   memcpy(data, Rdata, 16*sizeof(T));
   return *this;
}

template<typename T>
matrix<T> matrix<T>::operator*(T pT)
{
	T Rdata[16];
   memcpy(Rdata, data, 16 * sizeof(T));
	for(k = 0; k < 16; k++)
   	Rdata[k] *= pT;
   return matrix<T>(Rdata);
}

template<typename T>
vector<T> matrix<T>::operator*(const vector<T> &pV)
{
	T vdata[4], pvdata[4];
   pvdata[0] = pV.x;
   pvdata[1] = pV.y;
   pvdata[2] = pV.z;
   pvdata[3] = 0;
   for (col = 0; col < 4; col++)
   	{
   	vdata[row] = 0;
      k=0;
   	for (row = 0; row < 4; row++, k+=4)
   		vdata[col] += data[k + col]*pvdata[row];
      }
	return vector<T>(vdata);
} /**/

template<typename T>
Array<T, 3> matrix<T>::operator*(const Array<T, 3> &pV)
{
	T vdata[4], pvdata[4];

   memcpy(pvdata, pV.data, 3*sizeof(T));
   pvdata[3] = 1.0f;

   for (col = 0; col < 4; col++)
   	{
      k = 0;
   	vdata[col] = 0;
   	for (row = 0; row < 4; row++, k+=4)
   		vdata[col] += data[k + col]*pvdata[row];
      }

	return Array<T, 3>(vdata);
}

};

//#include "framework\Frm_3dMath.cpp"

#endif
